#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
专业回测系统集成模块
Professional Backtest System Integration

专业级回测功能升级 - 系统集成模块
整合多策略引擎、交易成本模拟、绩效分析、基准对比、可视化等组件

作者: 系统架构师 + 后端工程师 + 量化分析师
日期: 2025-08-04
"""

import logging
import numpy as np
import pandas as pd
from datetime import datetime, timedelta
from typing import Dict, List, Optional, Any, Tuple
import json
import os
from pathlib import Path

# 导入专业回测组件
from .multi_strategy_engine import MultiStrategyBacktestEngine, StrategyConfig
from .trading_cost_simulator import TradingCostSimulator, TradingCostConfig, Order, OrderSide
from .professional_performance_analyzer import ProfessionalPerformanceAnalyzer, PerformanceMetrics
from .benchmark_analyzer import BenchmarkAnalyzer, BenchmarkType
from .professional_visualization import ProfessionalVisualizer
from .constraints import next_execution_date, can_sell_t_plus_one, can_execute_order
from .market_data_provider import DailyMarketDataProvider

logger = logging.getLogger('户部尚赢.专业回测.系统集成')

class ProfessionalBacktestSystem:
    """专业回测系统"""

    def __init__(self, config: Dict[str, Any] = None):
        """初始化专业回测系统"""
        self.config = config or {}

        # 初始化核心组件
        self.multi_strategy_engine = MultiStrategyBacktestEngine(
            config.get('multi_strategy', {})
        )

        self.trading_cost_simulator = TradingCostSimulator(
            TradingCostConfig(**config.get('trading_cost', {}))
        )

        self.performance_analyzer = ProfessionalPerformanceAnalyzer(
            risk_free_rate=config.get('risk_free_rate', 0.03),
            trading_days=config.get('trading_days', 252)
        )

        self.benchmark_analyzer = BenchmarkAnalyzer()

        # 可视化器
        self.visualizer = ProfessionalVisualizer(
            style=self.config.get('visualization_style', 'professional')
        )


        # 统计真实约束影响
        self.constraint_stats = {
            'blocked_buys': 0,
            'blocked_sells': 0,
            'sell_deferrals': 0
        }

        # 交易约束配置（可由GUI更新）
        self.constraints = self.config.get('constraints', {
            'signal_delay_days': 1,
            'enforce_t_plus_one': True,
            'enforce_limits': True,
            'enforce_suspension': True
        })

        # 系统状态
        self.backtest_results = {}
        self.current_session_id = None

        # 输出目录
        self.output_dir = Path(config.get('output_dir', 'backtest_results'))
        self.output_dir.mkdir(exist_ok=True)

        logger.info("✅ 专业回测系统初始化完成")

    def create_backtest_session(self, session_name: str = None) -> str:
        """创建回测会话"""
        try:
            if session_name is None:
                session_name = f"backtest_{datetime.now().strftime('%Y%m%d_%H%M%S')}"

            self.current_session_id = session_name
            self.backtest_results[session_name] = {
                'created_at': datetime.now(),
                'strategies': {},
                'performance_metrics': {},
                'benchmark_comparisons': {},
                'visualizations': {},
                'reports': {}
            }

            # 创建会话输出目录
            session_dir = self.output_dir / session_name
            session_dir.mkdir(exist_ok=True)

            logger.info(f"✅ 回测会话创建完成: {session_name}")
            return session_name

        except Exception as e:
            logger.error(f"❌ 创建回测会话失败: {e}")
            return ""

    def add_strategy_to_session(self, strategy_config: StrategyConfig,
                              session_id: str = None) -> bool:
        """添加策略到会话"""
        try:
            session_id = session_id or self.current_session_id
            if not session_id or session_id not in self.backtest_results:
                logger.error(f"无效的会话ID: {session_id}")
                return False

            # 添加策略到多策略引擎
            success = self.multi_strategy_engine.add_strategy(strategy_config)

            if success:
                # 记录到会话
                self.backtest_results[session_id]['strategies'][strategy_config.name] = {
                    'config': strategy_config,
                    'added_at': datetime.now()
                }

                logger.info(f"✅ 策略已添加到会话: {strategy_config.name}")
                return True

            return False

        except Exception as e:
            logger.error(f"❌ 添加策略到会话失败: {e}")
            return False

    def run_comprehensive_backtest(self, start_date: datetime, end_date: datetime,
                                 initial_capital: float = 1000000.0,
                                 benchmark_types: List[BenchmarkType] = None,
                                 session_id: str = None) -> Dict[str, Any]:
        """运行综合回测分析"""
        try:
            session_id = session_id or self.current_session_id
            if not session_id:
                logger.error("没有活动的回测会话")
                return {'success': False, 'error': '没有活动的回测会话'}

            logger.info(f"🚀 开始综合回测分析: {session_id}")

            # 1. 运行多策略并行回测
            logger.info("📊 第1步: 执行多策略并行回测...")
            backtest_result = self.multi_strategy_engine.run_parallel_backtest(
                start_date, end_date, initial_capital
            )

            if not backtest_result['success']:
                return backtest_result

            # 2. 加载基准数据
            logger.info("📈 第2步: 加载基准数据...")
            if benchmark_types is None:
                benchmark_types = [BenchmarkType.CSI300, BenchmarkType.CSI500]

            for benchmark_type in benchmark_types:
                self.benchmark_analyzer.load_benchmark_data(
                    benchmark_type, start_date, end_date
                )

            # 3. 计算专业绩效指标
            logger.info("📊 第3步: 计算专业绩效指标...")
            performance_results = {}

            # 为每个策略计算绩效指标
            for strategy_name, strategy_result in backtest_result['strategy_results'].items():
                metrics = self.performance_analyzer.analyze_performance(
                    equity_curve=strategy_result.equity_curve,
                    trades=strategy_result.trades
                )
                performance_results[strategy_name] = metrics

            # 计算组合绩效指标
            if backtest_result['combined_results']:
                combined_equity = backtest_result['combined_results']['combined_equity_curve']
                combined_trades = backtest_result['combined_results']['all_trades']

                portfolio_metrics = self.performance_analyzer.analyze_performance(
                    equity_curve=combined_equity,
                    trades=combined_trades
                )
                performance_results['Portfolio'] = portfolio_metrics

            # 4. 基准对比分析
            logger.info("📈 第4步: 基准对比分析...")
            benchmark_comparisons = {}

            if backtest_result['combined_results']:
                combined_equity = backtest_result['combined_results']['combined_equity_curve']

                for benchmark_type in benchmark_types:
                    comparison = self.benchmark_analyzer.compare_with_benchmark(
                        combined_equity, benchmark_type
                    )
                    benchmark_comparisons[benchmark_type.name] = comparison

            # 5. 交易成本分析
            logger.info("💰 第5步: 交易成本分析...")
            trading_cost_analysis = self._analyze_trading_costs(backtest_result)
            trading_cost_analysis['constraints'] = self.constraints

            # 6. 生成可视化图表
            logger.info("📊 第6步: 生成可视化图表...")
            visualizations = self._generate_visualizations(
                backtest_result, performance_results, benchmark_comparisons
            )

            # 7. 生成综合报告
            logger.info("📋 第7步: 生成综合报告...")
            comprehensive_report = self._generate_comprehensive_report(
                backtest_result, performance_results, benchmark_comparisons,
                trading_cost_analysis, session_id
            )

            # 8. 保存结果到会话
            self.backtest_results[session_id].update({
                'backtest_result': backtest_result,
                'performance_metrics': performance_results,
                'benchmark_comparisons': benchmark_comparisons,
                'trading_cost_analysis': trading_cost_analysis,
                'visualizations': visualizations,
                'comprehensive_report': comprehensive_report,
                'completed_at': datetime.now()
            })

            # 9. 保存到文件
            self._save_session_results(session_id)

            logger.info("✅ 综合回测分析完成")

            return {
                'success': True,
                'session_id': session_id,
                'backtest_result': backtest_result,
                'performance_metrics': performance_results,
                'benchmark_comparisons': benchmark_comparisons,
                'trading_cost_analysis': trading_cost_analysis,
                'comprehensive_report': comprehensive_report,
                'output_dir': str(self.output_dir / session_id)
            }

        except Exception as e:
            logger.error(f"❌ 综合回测分析失败: {e}")
            return {'success': False, 'error': str(e)}

    def _analyze_trading_costs(self, backtest_result: Dict) -> Dict[str, Any]:
        """分析交易成本"""
        try:
            logger.info("💰 分析交易成本...")

            total_cost_analysis = {
                'total_trading_cost': 0.0,
                'avg_cost_per_trade': 0.0,
                'cost_as_percentage_of_pnl': 0.0,
                'strategy_costs': {}
            }

            total_trades = 0
            total_cost = 0.0
            total_pnl = 0.0

            # 分析每个策略的交易成本
            for strategy_name, strategy_result in backtest_result['strategy_results'].items():
                trades = strategy_result.trades
                strategy_cost = 0.0
                strategy_pnl = 0.0

                for trade in trades:
                    # 创建买入订单
                    buy_order = Order(
                        stock_code=trade.get('stock_code', '000000'),
                        side=OrderSide.BUY,
                        quantity=trade.get('quantity', 1000),
                        price=trade.get('entry_price', 10.0),
                        timestamp=pd.to_datetime(trade.get('entry_date', datetime.now()))
                    )

                    # 创建卖出订单
                    sell_order = Order(
                        stock_code=trade.get('stock_code', '000000'),
                        side=OrderSide.SELL,
                        quantity=trade.get('quantity', 1000),
                        price=trade.get('exit_price', 10.0),
                        timestamp=pd.to_datetime(trade.get('exit_date', datetime.now()))
                    )

                    # 应用最小交易约束：信号延迟与T+1（后续可扩展涨跌停/停牌，需引入逐日行情）
                    delay_days = int(self.constraints.get('signal_delay_days', 1) or 0)
                    adjusted_entry_ts = next_execution_date(
                        pd.to_datetime(trade.get('entry_date', datetime.now())), delay_days
                    ) if delay_days > 0 else pd.to_datetime(trade.get('entry_date', datetime.now()))
                    adjusted_exit_ts = pd.to_datetime(trade.get('exit_date', datetime.now()))

                    if self.constraints.get('enforce_t_plus_one', True):
                        if not can_sell_t_plus_one(adjusted_entry_ts, adjusted_exit_ts):
                            adjusted_exit_ts = next_execution_date(adjusted_entry_ts, 1)

                    buy_order.timestamp = pd.to_datetime(adjusted_entry_ts)
                    sell_order.timestamp = pd.to_datetime(adjusted_exit_ts)

                    # 真实市场约束：涨跌停/停牌（需要日行情）
                    try:
                        mdp = getattr(self, "_market_data_provider", None) or DailyMarketDataProvider()
                        self._market_data_provider = mdp
                        # 以开盘价模拟买入、收盘价模拟卖出可根据策略调整
                        entry_state = mdp.get_market_state(buy_order.stock_code, buy_order.timestamp)
                        exit_state = mdp.get_market_state(sell_order.stock_code, sell_order.timestamp)
                        if entry_state and self.constraints.get('enforce_limits', True):
                            if not can_execute_order('buy', float(trade.get('entry_price', 10.0)), entry_state,
                                                     is_suspended_ok=not self.constraints.get('enforce_suspension', True)):
                                # 若买入被阻断，则该笔交易视为未发生
                                continue
                        if exit_state and self.constraints.get('enforce_limits', True):
                            if not can_execute_order('sell', float(trade.get('exit_price', 10.0)), exit_state,
                                                     is_suspended_ok=not self.constraints.get('enforce_suspension', True)):
                                # 卖出被阻断：顺延到下一执行日（简化处理）
                                from .constraints import next_execution_date as _next
                                sell_order.timestamp = _next(sell_order.timestamp, 1)
                    except Exception:
                        # 数据缺失或提供者异常时，保持兼容，继续计算成本
                        pass

                    # 计算交易成本
                    buy_cost = self.trading_cost_simulator.calculate_transaction_cost(buy_order)
                    sell_cost = self.trading_cost_simulator.calculate_transaction_cost(sell_order)

                    trade_cost = buy_cost.total_cost + sell_cost.total_cost
                    strategy_cost += trade_cost
                    strategy_pnl += trade.get('pnl', 0)

                total_cost += strategy_cost
                total_pnl += strategy_pnl
                total_trades += len(trades)

                total_cost_analysis['strategy_costs'][strategy_name] = {
                    'total_cost': strategy_cost,
                    'trades_count': len(trades),
                    'avg_cost_per_trade': strategy_cost / len(trades) if trades else 0,
                    'cost_as_percentage_of_pnl': (strategy_cost / abs(strategy_pnl) * 100) if strategy_pnl != 0 else 0
                }

            # 总体成本分析
            total_cost_analysis.update({
                'total_trading_cost': total_cost,
                'avg_cost_per_trade': total_cost / total_trades if total_trades > 0 else 0,
                'cost_as_percentage_of_pnl': (total_cost / abs(total_pnl) * 100) if total_pnl != 0 else 0,
                'total_trades': total_trades
            })

            logger.info(f"✅ 交易成本分析完成: 总成本 {total_cost:.2f}元")
            return total_cost_analysis

        except Exception as e:
            logger.error(f"❌ 交易成本分析失败: {e}")
            return {}

    def _generate_visualizations(self, backtest_result: Dict,
                               performance_results: Dict,
                               benchmark_comparisons: Dict) -> Dict[str, Any]:
        """生成可视化图表"""
        try:
            logger.info("📊 生成可视化图表...")

            visualizations = {}

            if backtest_result['combined_results']:
                combined_equity = backtest_result['combined_results']['combined_equity_curve']
                combined_returns = backtest_result['combined_results']['combined_daily_returns']

                # 获取基准数据（如果有）
                benchmark_equity = None
                if benchmark_comparisons:
                    # 使用第一个基准作为对比
                    first_benchmark = list(benchmark_comparisons.keys())[0]
                    benchmark_type = BenchmarkType[first_benchmark]
                    if benchmark_type in self.benchmark_analyzer.benchmark_data:
                        benchmark_equity = self.benchmark_analyzer.benchmark_data[benchmark_type]

                # 创建综合仪表板
                dashboard = self.visualizer.create_comprehensive_dashboard(
                    strategy_equity=combined_equity,
                    benchmark_equity=benchmark_equity,
                    returns=combined_returns,
                    metrics=performance_results
                )

                visualizations['dashboard'] = dashboard

                # 策略对比图表
                if len(backtest_result['strategy_results']) > 1:
                    strategy_equities = {}
                    for name, result in backtest_result['strategy_results'].items():
                        strategy_equities[name] = result.equity_curve

                    comparison_chart = self.visualizer.plot_equity_curve(
                        list(strategy_equities.values())[0],  # 主策略
                        title="多策略净值对比"
                    )
                    visualizations['strategy_comparison'] = comparison_chart

            logger.info(f"✅ 可视化图表生成完成: {len(visualizations)} 个图表")
            return visualizations

        except Exception as e:
            logger.error(f"❌ 生成可视化图表失败: {e}")
            return {}

    def _generate_comprehensive_report(self, backtest_result: Dict,
                                     performance_results: Dict,
                                     benchmark_comparisons: Dict,
                                     trading_cost_analysis: Dict,
                                     session_id: str) -> Dict[str, Any]:
        """生成综合报告"""
        try:
            logger.info("📋 生成综合报告...")

            report = {
                'session_info': {
                    'session_id': session_id,
                    'generated_at': datetime.now().isoformat(),
                    'strategies_count': len(backtest_result['strategy_results']),
                    'execution_time': backtest_result.get('execution_time', 0)
                },
                'executive_summary': {},
                'strategy_performance': {},
                'benchmark_analysis': {},
                'trading_cost_summary': trading_cost_analysis,
                'risk_analysis': {},
                'recommendations': []
            }

            # 执行摘要
            if 'Portfolio' in performance_results:
                portfolio_metrics = performance_results['Portfolio']
                report['executive_summary'] = {
                    'total_return': f"{portfolio_metrics.total_return:.2%}",
                    'annualized_return': f"{portfolio_metrics.annualized_return:.2%}",
                    'volatility': f"{portfolio_metrics.volatility:.2%}",
                    'sharpe_ratio': f"{portfolio_metrics.sharpe_ratio:.3f}",
                    'max_drawdown': f"{portfolio_metrics.max_drawdown:.2%}",
                    'win_rate': f"{portfolio_metrics.win_rate:.2%}",
                    'total_trades': portfolio_metrics.total_trades
                }

            # 策略绩效详情
            for strategy_name, metrics in performance_results.items():
                report['strategy_performance'][strategy_name] = self.performance_analyzer.generate_performance_report(metrics)

            # 基准分析摘要
            if benchmark_comparisons:
                report['benchmark_analysis'] = self.benchmark_analyzer.generate_benchmark_report(benchmark_comparisons)

            # 风险分析
            if 'Portfolio' in performance_results:
                portfolio_metrics = performance_results['Portfolio']
                report['risk_analysis'] = {
                    'var_95': f"{portfolio_metrics.var_95:.4f}",
                    'cvar_95': f"{portfolio_metrics.cvar_95:.4f}",
                    'max_drawdown_duration': f"{portfolio_metrics.max_drawdown_duration} 天",
                    'downside_volatility': f"{portfolio_metrics.downside_volatility:.2%}",
                    'tail_ratio': f"{portfolio_metrics.tail_ratio:.2f}"
                }

            # 生成建议
            report['recommendations'] = self._generate_recommendations(
                performance_results, benchmark_comparisons, trading_cost_analysis
            )

            logger.info("✅ 综合报告生成完成")
            return report

        except Exception as e:
            logger.error(f"❌ 生成综合报告失败: {e}")
            return {}

    def _generate_recommendations(self, performance_results: Dict,
                                benchmark_comparisons: Dict,
                                trading_cost_analysis: Dict) -> List[str]:
        """生成投资建议"""
        try:
            recommendations = []

            # 基于绩效指标的建议
            if 'Portfolio' in performance_results:
                metrics = performance_results['Portfolio']

                if metrics.sharpe_ratio > 1.5:
                    recommendations.append("✅ 策略夏普比率优秀，风险调整后收益表现良好")
                elif metrics.sharpe_ratio < 0.5:
                    recommendations.append("⚠️ 策略夏普比率偏低，建议优化风险控制")

                if metrics.max_drawdown < -0.1:
                    recommendations.append("⚠️ 最大回撤较大，建议加强止损机制")

                if metrics.win_rate > 0.6:
                    recommendations.append("✅ 胜率较高，策略选股能力较强")
                elif metrics.win_rate < 0.4:
                    recommendations.append("⚠️ 胜率偏低，建议优化选股条件")

            # 基于基准对比的建议
            if benchmark_comparisons:
                avg_excess_return = np.mean([comp.excess_return for comp in benchmark_comparisons.values()])
                if avg_excess_return > 0.05:
                    recommendations.append("✅ 策略相对基准有显著超额收益")
                elif avg_excess_return < 0:
                    recommendations.append("⚠️ 策略跑输基准，建议重新评估策略有效性")

            # 基于交易成本的建议
            if trading_cost_analysis:
                cost_percentage = trading_cost_analysis.get('cost_as_percentage_of_pnl', 0)
                if cost_percentage > 10:
                    recommendations.append("⚠️ 交易成本占收益比例较高，建议降低交易频率")
                elif cost_percentage < 2:
                    recommendations.append("✅ 交易成本控制良好")

            if not recommendations:
                recommendations.append("📊 策略表现正常，建议继续监控")

            return recommendations

        except Exception as e:
            logger.error(f"❌ 生成建议失败: {e}")
            return ["❌ 无法生成建议"]

    def _save_session_results(self, session_id: str):
        """保存会话结果到文件"""
        try:
            session_dir = self.output_dir / session_id
            session_data = self.backtest_results[session_id]

            # 保存综合报告
            report_path = session_dir / "comprehensive_report.json"
            with open(report_path, 'w', encoding='utf-8') as f:
                # 序列化报告（排除不可序列化的对象）
                serializable_report = self._make_serializable(session_data.get('comprehensive_report', {}))
                json.dump(serializable_report, f, ensure_ascii=False, indent=2)

            # 保存可视化图表
            if 'visualizations' in session_data and 'dashboard' in session_data['visualizations']:
                dashboard = session_data['visualizations']['dashboard']
                html_path = session_dir / "backtest_dashboard.html"
                self.visualizer.save_figures_to_html(dashboard, str(html_path))

            logger.info(f"✅ 会话结果已保存: {session_dir}")

        except Exception as e:
            logger.error(f"❌ 保存会话结果失败: {e}")

    def _make_serializable(self, obj):
        """使对象可序列化"""
        if isinstance(obj, dict):
            return {k: self._make_serializable(v) for k, v in obj.items()}
        elif isinstance(obj, list):
            return [self._make_serializable(item) for item in obj]
        elif isinstance(obj, (pd.Series, pd.DataFrame)):
            return obj.to_dict()
        elif isinstance(obj, datetime):
            return obj.isoformat()
        elif isinstance(obj, (np.integer, np.floating)):
            return float(obj)
        else:
            return obj

    def get_session_summary(self, session_id: str = None) -> Dict[str, Any]:
        """获取会话摘要"""
        try:
            session_id = session_id or self.current_session_id
            if not session_id or session_id not in self.backtest_results:
                return {}

            session_data = self.backtest_results[session_id]

            summary = {
                'session_id': session_id,
                'created_at': session_data.get('created_at'),
                'completed_at': session_data.get('completed_at'),
                'strategies_count': len(session_data.get('strategies', {})),
                'status': 'completed' if 'completed_at' in session_data else 'running'
            }

            # 添加关键绩效指标
            if 'comprehensive_report' in session_data:
                report = session_data['comprehensive_report']
                if 'executive_summary' in report:
                    summary['key_metrics'] = report['executive_summary']

            return summary

        except Exception as e:
            logger.error(f"❌ 获取会话摘要失败: {e}")
            return {}

    def list_sessions(self) -> List[Dict[str, Any]]:
        """列出所有会话"""
        try:
            sessions = []
            for session_id in self.backtest_results.keys():
                summary = self.get_session_summary(session_id)
                if summary:
                    sessions.append(summary)

            # 按创建时间排序
            sessions.sort(key=lambda x: x.get('created_at', datetime.min), reverse=True)
            return sessions

        except Exception as e:
            logger.error(f"❌ 列出会话失败: {e}")
            return []

# 使用示例
if __name__ == "__main__":
    # 创建专业回测系统
    config = {
        'risk_free_rate': 0.03,
        'trading_days': 252,
        'trading_cost': {
            'commission_rate': 0.0003,
            'base_slippage_bps': 2.0
        },
        'output_dir': 'professional_backtest_results'
    }

    system = ProfessionalBacktestSystem(config)

    # 创建回测会话
    session_id = system.create_backtest_session("demo_backtest")

    # 添加策略
    strategy1 = StrategyConfig(
        name="底部反转策略",
        strategy_type="bottom_reversal",
        parameters={"min_rebound": 0.05},
        weight=0.6
    )

    strategy2 = StrategyConfig(
        name="涨停追击策略",
        strategy_type="limit_up_chase",
        parameters={"min_change": 0.095},
        weight=0.4
    )

    system.add_strategy_to_session(strategy1)
    system.add_strategy_to_session(strategy2)

    # 运行综合回测
    start_date = datetime(2024, 1, 1)
    end_date = datetime(2024, 12, 31)

    result = system.run_comprehensive_backtest(
        start_date=start_date,
        end_date=end_date,
        initial_capital=1000000.0,
        benchmark_types=[BenchmarkType.CSI300, BenchmarkType.CSI500]
    )

    if result['success']:
        print("✅ 专业回测系统运行完成")
        print(f"会话ID: {result['session_id']}")
        print(f"输出目录: {result['output_dir']}")

        # 获取会话摘要
        summary = system.get_session_summary()
        print(f"策略数量: {summary['strategies_count']}")
        print(f"状态: {summary['status']}")
    else:
        print(f"❌ 回测失败: {result['error']}")
